import { createClient } from 'redis';
import { promisify } from 'util';
import NodeCache from 'node-cache';

interface CacheConfig {
  ttl: number;  // 缓存过期时间(秒)
  checkperiod?: number;  // 检查过期数据的周期(秒)
  useRedis?: boolean;  // 是否使用Redis
  redisUrl?: string;  // Redis连接URL
}

export class CacheService {
  private static instance: CacheService;
  private localCache: NodeCache;
  private redisClient: ReturnType<typeof createClient> | null = null;
  private readonly config: CacheConfig;

  private constructor(config: CacheConfig) {
    this.config = config;
    this.localCache = new NodeCache({
      stdTTL: config.ttl,
      checkperiod: config.checkperiod || config.ttl * 0.2
    });

    if (config.useRedis) {
      this.initRedis();
    }
  }

  public static getInstance(config?: CacheConfig): CacheService {
    if (!CacheService.instance) {
      CacheService.instance = new CacheService(config || {
        ttl: 300,  // 默认5分钟
        useRedis: false
      });
    }
    return CacheService.instance;
  }

  private async initRedis(): Promise<void> {
    try {
      this.redisClient = createClient({
        url: this.config.redisUrl || process.env.REDIS_URL
      });

      this.redisClient.on('error', (err) => {
        console.error('Redis Client Error:', err);
        // 如果Redis连接失败，回退到本地缓存
        this.redisClient = null;
      });

      await this.redisClient.connect();
    } catch (error) {
      console.error('Failed to initialize Redis:', error);
      this.redisClient = null;
    }
  }

  // 获取缓存数据
  public async get<T>(key: string): Promise<T | null> {
    try {
      // 优先从Redis获取
      if (this.redisClient) {
        const value = await this.redisClient.get(key);
        if (value) {
          return JSON.parse(value);
        }
      }

      // 从本地缓存获取
      return this.localCache.get<T>(key) || null;
    } catch (error) {
      console.error('Cache get error:', error);
      return null;
    }
  }

  // 设置缓存数据
  public async set(key: string, value: any, ttl?: number): Promise<void> {
    try {
      const actualTtl = ttl || this.config.ttl;

      // 同时写入Redis和本地缓存
      if (this.redisClient) {
        await this.redisClient.setEx(
          key,
          actualTtl,
          JSON.stringify(value)
        );
      }

      this.localCache.set(key, value, actualTtl);
    } catch (error) {
      console.error('Cache set error:', error);
    }
  }

  // 删除缓存数据
  public async delete(key: string): Promise<void> {
    try {
      if (this.redisClient) {
        await this.redisClient.del(key);
      }
      this.localCache.del(key);
    } catch (error) {
      console.error('Cache delete error:', error);
    }
  }

  // 清空所有缓存
  public async clear(): Promise<void> {
    try {
      if (this.redisClient) {
        await this.redisClient.flushAll();
      }
      this.localCache.flushAll();
    } catch (error) {
      console.error('Cache clear error:', error);
    }
  }

  // 批量获取缓存
  public async mget<T>(keys: string[]): Promise<Record<string, T | null>> {
    try {
      const result: Record<string, T | null> = {};

      if (this.redisClient) {
        const values = await this.redisClient.mGet(keys);
        keys.forEach((key, index) => {
          const value = values[index];
          result[key] = value ? JSON.parse(value) : null;
        });
      } else {
        keys.forEach(key => {
          result[key] = this.localCache.get<T>(key) || null;
        });
      }

      return result;
    } catch (error) {
      console.error('Cache mget error:', error);
      return {};
    }
  }

  // 批量设置缓存
  public async mset(data: Record<string, any>, ttl?: number): Promise<void> {
    try {
      const actualTtl = ttl || this.config.ttl;

      if (this.redisClient) {
        const pipeline = this.redisClient.multi();
        Object.entries(data).forEach(([key, value]) => {
          pipeline.setEx(key, actualTtl, JSON.stringify(value));
        });
        await pipeline.exec();
      }

      Object.entries(data).forEach(([key, value]) => {
        this.localCache.set(key, value, actualTtl);
      });
    } catch (error) {
      console.error('Cache mset error:', error);
    }
  }

  // 获取或设置缓存（如果缓存不存在，则调用回调函数获取数据）
  public async getOrSet<T>(
    key: string,
    callback: () => Promise<T>,
    ttl?: number
  ): Promise<T | null> {
    try {
      const cached = await this.get<T>(key);
      if (cached !== null) {
        return cached;
      }

      const value = await callback();
      if (value !== null) {
        await this.set(key, value, ttl);
      }

      return value;
    } catch (error) {
      console.error('Cache getOrSet error:', error);
      return null;
    }
  }

  // 缓存预热
  public async warmup<T>(
    keys: string[],
    callback: (key: string) => Promise<T>
  ): Promise<void> {
    try {
      const missingKeys = keys.filter(async key => 
        (await this.get(key)) === null
      );

      const values = await Promise.all(
        missingKeys.map(async key => ({
          key,
          value: await callback(key)
        }))
      );

      const data = values.reduce((acc, { key, value }) => {
        if (value !== null) {
          acc[key] = value;
        }
        return acc;
      }, {} as Record<string, T>);

      await this.mset(data);
    } catch (error) {
      console.error('Cache warmup error:', error);
    }
  }

  // 监控缓存状态
  public getStats() {
    return {
      local: this.localCache.getStats(),
      redis: this.redisClient ? 'connected' : 'disconnected'
    };
  }
}
